#pragma once
#include <string>
#include <unordered_map>
#include <memory>
#include <sstream>
#include <functional>
#include "Socket.hpp"
#include "InetAddr.hpp"
#include "Log.hpp"
#include "Util.hpp"

using namespace SocketModule;
using namespace LogModule;

const std::string gspace = " ";        // 请求行空格
const std::string glinespace = "\r\n"; // 换行符
const std::string gsep = ": ";         // 请求报头的kv分隔符

const std::string gwebroot = "./wwwroot";
const std::string ghomepage = "index.html";
const std::string gnfindpage = "/404.html";

const std::string ghttpversion = "HTTP/1.1";

class HttpRequest;
class HttpResponse;
using http_service_t = std::function<void(HttpRequest &, HttpResponse &)>;

class HttpRequest
{
public:
    HttpRequest() : _blankline(glinespace)
    {
    }

    // req_line: GET空格/空格HTTP/1.1\r\n
    void PraseReqLine(const std::string &req_line)
    {
        std::stringstream ss(req_line);
        ss >> _method >> _uri >> _version;
    }

    bool IsFetchStaticResources(const std::string &uri)
    {
        auto pos = uri.find(".");
        if (pos == std::string::npos)
            return false;
        return true;
    }

    bool Deserialize(std::string &in)
    {
        // 解析请求行
        std::string req_line;
        Util::ReadOneLine(in, req_line, glinespace);
        // 设置请求参数
        PraseReqLine(req_line);
        LOG(LogLevel::INFO) << "method:" << _method;
        LOG(LogLevel::INFO) << "uri:" << _uri;
        LOG(LogLevel::INFO) << "version:" << _version;

        if (_uri == "/")
            _uri = gwebroot + _uri + ghomepage; // ./wwwroot + /index.html
        else if (IsFetchStaticResources(_uri))
            _uri = gwebroot + _uri; // ./wwwroot + /a/b/c.html
        else
            ; // _uri不变，/login?name=zhangsan&passwd=123456

        // 读取报头
        std::string line;
        // 按行读，直到读到空行为止
        while (Util::ReadOneLine(in, line, glinespace) != "")
        {
            // key: value
            size_t pos = line.find(gsep);
            std::string key = line.substr(0, pos);
            std::string value = line.substr(pos + gsep.size());
            _headers.emplace(key, value);
        }

        //设置正文数据
        _text = in;

        return true;
    }

    // 为GET方法设置参数
    void SetArgs()
    {
        // /login?name=zhangsan&age=18
        if (_method == "GET")
        {
            size_t pos = _uri.find("?");
            if (pos != std::string::npos)
                _args = _uri.substr(pos + 1);
        }
        else if (_method == "POST")
        {
            _args = _text;
        }
    }

    std::string Uri() { return _uri; }
    std::string Args() { return _args; }

    ~HttpRequest()
    {
    }

    // public:
private:
    std::string _method;  // 请求方法
    std::string _uri;     // uri（资源标识符），资源路径
    std::string _version; // HTTP版本
    std::unordered_map<std::string, std::string> _headers;

    std::string _blankline; // 空行
    std::string _text;      // 正文数据，有效载荷

    std::string _args; // 不属于报文的结构，原始GET请求参数
};

class HttpResponse
{
public:
    HttpResponse() : _blankline(glinespace), _version(ghttpversion) {}
    std::string Serialize()
    {
        // 字符串拼接
        std::string result = _version + gspace + std::to_string(_code) + gspace + _description + glinespace;
        for (auto &header : _headers)
        {
            result += header.first + gsep + header.second + glinespace;
        }
        result += _blankline + _text;
        return result;
    }
    void SetCode(int code)
    {
        _code = code;
        switch (code)
        {
        case 404:
            _description = "Not Found";
            break;
        case 200:
            _description = "OK";
            break;
        case 302:
            _description = "Found or See Other";
            break;
        default:
            _description = "Not Known";
            break;
        }
    }
    void SetHeader(const std::string key, const std::string value)
    {
        _headers.emplace(key, value);
    }

    std::string GetContentType(const std::string &filepath)
    {
        auto pos = filepath.rfind(".");
        if (pos == std::string::npos)
            return "";

        std::string suffix = filepath.substr(pos);
        if (suffix == ".html")
            return "text/html";
        else if (suffix == ".jpg")
            return "image/jpeg";
        else if (suffix == ".png")
            return "image/png";

        return "";
    }

    bool IsFetchStaticResources(const std::string &uri)
    {
        size_t pos = uri.rfind(".");
        // /login/xxxxx 找不到点
        if (pos == std::string::npos)
            return false;
        return true;
    }

    std::string GetService(const std::string &uri)
    {
        // /login?name=zhangsan&age=18
        size_t pos = uri.find("?");
        return uri.substr(0, pos);
    }

    void Set404Page(const std::string &uri)
    {
        SetCode(302);
        SetHeader("Location", gnfindpage);
        LOG(LogLevel::INFO) << "要访问的资源不存在：" << uri;
    }

    bool MakeResponse(const std::string &uri,
                      const std::unordered_map<std::string, http_service_t> &httpservices,
                      http_service_t *h)
    {
        // 判断获取的是否是静态资源
        if (IsFetchStaticResources(uri))
        {
            int filesize = Util::ReadFileContent(uri, _text);
            if (filesize < 0)
            {
                Set404Page(uri);
                // SetCode(404);
                // LOG(LogLevel::INFO) << "要访问的资源不存在：" << filepath;
                // int nfpagesize = Util::ReadFileContent(gwebroot + gnfindpage, _text);
                // SetHeader("Content-Length", std::to_string(nfpagesize));
                // SetHeader("Content-Type", "text/html");
            }
            else
            {
                SetCode(200);
                SetHeader("Content-Length", std::to_string(filesize));
                SetHeader("Content-Type", GetContentType(uri));
            }
        }
        else
        {
            // /redir_test
            if (uri == "/redir_test")
            {
                SetCode(302);
                // SetHeader("Location", "https://www.qq.com/");
                SetHeader("Location", "/image/5.jpg");
            }
            else
            {
                // /login?name=zhangsan&passwd=123456
                std::string req_service = GetService(uri);
                auto iter = httpservices.find(req_service);
                if (iter == httpservices.end())
                    Set404Page(uri);
                else
                {
                    *h = iter->second;
                    return false; // 需要在外部调用回调处理req_service方法
                }
            }
        }
        return true;
    }

    ~HttpResponse() {}

    // private:
public:
    std::string _version;     // Http版本
    int _code;                // 状态码
    std::string _description; // 状态码描述
    std::unordered_map<std::string, std::string> _headers;

    std::string _blankline;
    std::string _text;
};

class Http
{
public:
    Http()
    {
    }
    void Register(const std::string &name, http_service_t t)
    {
        // 不存在就插入
        auto iter = _httpservices.find(name);
        if (iter == _httpservices.end())
            _httpservices.emplace(name, t);
    }

    void GetRequest(std::shared_ptr<Socket> sock, const InetAddr &addr)
    {
        // 1.接受请求
        std::string data;
        bool success = sock->Recv(data);
        if (success)
        {
            std::cout << "******************************" << std::endl;
            std::cout << data;
            std::cout << "******************************" << std::endl;

            // 2.1 验证请求完整性，略
            // 2.2 反序列化
            HttpRequest req;
            bool success = req.Deserialize(data);

            // 3.处理请求，返回结果
            std::string filepath = req.Uri();
            HttpResponse resp;
            // 获取服务的，需要回调处理
            http_service_t h;
            if (resp.MakeResponse(filepath, _httpservices, &h) == false)
            {
                req.SetArgs();
                h(req, resp);
            }

            // 4.序列化结果
            std::string result = resp.Serialize();

            // 5.返回响应
            ssize_t n = sock->Send(result);
            (void)n;
        }
    }

    ~Http()
    {
    }

private:
    std::unordered_map<std::string, http_service_t> _httpservices;
};